/*
 * -------------------------------------------------------------------------
 * 资源下载器
 * -------------------------------------------------------------------------
 */

const fs    = require('fs');
const http  = require('http');
const https = require('https');
const is    = require('./is');

/**
 * 获取链接数据（支持重定向）
 * @param url
 * @param saveFile
 * @param onProgress
 * @return {Promise<*>}
 */
function getUrl (url, saveFile, onProgress) {
    onProgress = is.func(onProgress) ? onProgress : (() => {});
    
    return new Promise((resovle, reject) => {
        try {
            /*数据流*/
            let stream;
            
            /*选择协议*/
            const protocol = url.startsWith('https') ? https : http;
            
            /*保存到文件*/
            saveFile && (stream = fs.createWriteStream(saveFile));
            
            /*发起请求*/
            const req = protocol.request(url, res => {
                /*文件长度*/
                let total = res.headers['content-length'];
                
                /*已下载*/
                let downloaded = 0;
                
                /*数据*/
                let data = '';
                
                /*重定向*/
                if (res.statusCode === 301 || res.statusCode === 302) {
                    /*跳转链接*/
                    let redirectUrl = res.headers.location;
                    
                    /*相对链接*/
                    const { protocol, hostname, port } = new URL(url);
                    if (!res.headers.location.startsWith('http')) {
                        redirectUrl = `${protocol}//${hostname}${port ? `:${port}` : ''}/${res.headers.location}`;
                    }
                    res.destroy();
                    return reject(retJson(2, '链接重定向', redirectUrl));
                }
                
                /*其他状态*/
                if (res.statusCode > 302) return reject(retJson(1, `通信失败 HttpCode ${res.statusCode}`));
                
                /*写数据、通知下载进度*/
                res.on('data', code => {
                    data = `${data}${code}`;
                    saveFile && stream.write(code);
                    downloaded += code.length;
                    onProgress(total, downloaded); /*更新下载进度*/
                });
                
                /*请求完成*/
                res.on('end', () => {
                    saveFile && stream.end();
                    return resovle(retJson(0, '文件下载成功', saveFile || data));
                });
                
                /*写入失败*/
                saveFile && stream.on('error', (e) => {
                    stream.destroy();
                    fs.existsSync(saveFile) && fs.unlinkSync(saveFile);
                    return reject(retJson(1, '文件写入失败', e.message));
                });
            });
            
            /*错误*/
            req.on('error', e => {
                saveFile && stream.destroy();
                (saveFile && fs.existsSync(saveFile)) && fs.unlinkSync(saveFile);
                return reject(retJson(1, '通信失败', e.message));
            });
            
            /*请求结束*/
            req.end();
        }
        catch (e) {
            return reject(retJson(1, '通信失败', e.message));
        }
    }).catch(res => {
        /*递归实现 页面重定向跟踪*/
        if (res.errCode === 2) return getUrl(res.data, saveFile, onProgress);
        return Promise.reject(res);
    });
}

/**
 * 保存链接数据到文件（支持重定向）
 * @param url
 * @param saveFile
 * @param onProgress
 * @return {Promise<*>}
 */
async function saveUrl (url, saveFile, onProgress) {
    if (!saveFile) return Promise.reject(retJson(1, '未指定保存路径'));
    return await getUrl(url, saveFile, onProgress);
}

/**
 * 返回值
 * @param errCode
 * @param errMsg
 * @param data
 * @return {{data, errCode, errMsg}}
 */
function retJson (errCode, errMsg, data) {
    data = data || '';
    return { errCode, errMsg, data };
}

/**
 * 导出
 * @type {{getUrl: (function(*=, *=): Promise<unknown>), saveUrl: ((function(*=, *=): Promise<*|commander.ParseOptionsResult.unknown>)|*)}}
 */
module.exports = {
    getUrl,
    saveUrl
};

